#include "widget.h"
#include "GL/gl.h"
#include <QImage>
#include <math.h>
#include <QTime>

#define STB_IMAGE_IMPLEMENTATION
#include "std_image.h"

QVector3D cameraPos   = QVector3D(0.0f, 0.0f,  5.0f); //摄像机初始化在z轴正方向
QVector3D cameraFront = QVector3D(0.0f, 0.0f,  -10.0f);
QVector3D cameraUp    = QVector3D(0.0f, 1.0f,  0.0f);

int deltaTime = 0; // 当前帧与上一帧的时间差
QTime lastTime;

float lastX = 0, lastY = 0;
float pitch = 0, yaw = -90, roll = 0; //初始化摄像机方向 俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll)
float fov = 45.0;

Widget::Widget(QOpenGLWidget *parent)
    :QOpenGLWidget(parent)
{
    setWindowTitle("立方体贴图");
    m_angle = 0.0;
    m_timer = new QTimer;
    m_timer->setInterval(30);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(timeout()));

    lastTime.start();
    setMouseTracking(true);
    firstMouse = 0;
    setWindowState(Qt::WindowMaximized);

    lastX = width()/2;
    lastY = height()/2;

    QPixmap p(1, 1);
    p.fill(Qt::transparent);

    m_cursor = new QCursor(p);

    setCursor(*m_cursor);
    lightShader = new LightShader(this);
    cubeShader = new CubeShader(this);
    glassShader = new GlassShader(this);

    m_timer->start();
}

Widget::~Widget()
{
}

void Widget::initializeGL()
{
    initializeOpenGLFunctions();

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_STENCIL_TEST);
    glEnable(GL_BLEND);
    glEnable(GL_CULL_FACE);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glFrontFace(GL_CCW); //逆时针 默认
    glCullFace(GL_BACK);

    //VAO，VBO数据部分
    float vertices[] = {
        // positions          // normals           // texture coords
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f
    };

    //创建立方体贴图
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

    int width, height, nrChannels;
    vector<string> textures_faces = {
        "../right.jpg", "../left.jpg", "../top.jpg",
        "../bottom.jpg", "../front.jpg", "../back.jpg"
    };
    for(unsigned int i = 0; i < textures_faces.size(); i++)
    {
        unsigned char *data = stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0);

        if (data)
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
                         0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data
                         );
            stbi_image_free(data);
        }
        else
        {
            qInfo() << "Cubemap texture failed to load at path: " << QString(textures_faces[i].c_str());
            stbi_image_free(data);
        }
    }

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);


    memcpy((char*)lightShader->vertices, (char*)vertices, sizeof(vertices));
    lightShader->initializeGL();

//    memcpy((char*)cubeShader->vertices, (char*)vertices, sizeof(vertices));
    cubeShader->skyboxTexture = textureID;
    cubeShader->initializeGL();
    cubeShader->lightColor = QVector3D(1.0f, 1.0f, 1.0f);

    cubeShader->viewPos = cameraPos;

    glassShader->width = 1920;
    glassShader->height = 1017;
    glassShader->textureID = textureID;
    glassShader->initializeGL();
}

void Widget::resizeGL(int w, int h)
{
    qInfo()<<__FUNCTION__<<w<<h;
    glViewport(0, 0, w, h);
    glassShader->width = width();
    glassShader->height = height();
}

void Widget::paintGL()
{
    deltaTime = lastTime.elapsed();
    lastTime.restart();

    glClearColor(0.2f, 0.3f, 0.3f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 projection, view;

    float ratio = (float)width() / (float)height();
    projection.perspective(fov, ratio, 0.1f, 100.0f);
    view.lookAt(cameraPos, cameraPos+cameraFront, cameraUp);



    QMatrix4x4 model;
    model.translate(QVector3D(0.0f,  0.0f,  0.0f));
//    model.rotate(0.0f, QVector3D(0.5f, 1.0f, 0.0f));

    cubeShader->projection = projection;
    cubeShader->view = view;
    cubeShader->model = model;
    cubeShader->paintGL();

    //天空盒
    QMatrix3x3 view2 = view.toGenericMatrix<3,3>(); // 移除位移
    glassShader->projection = projection;
    glassShader->view = QMatrix4x4(view2);
//    qInfo()<<"vvvvvv " << view << view2 << glassShader->view;
    glassShader->paintGL();

}

void Widget::keyPressEvent(QKeyEvent *event)
{
    float cameraSpeed = 0.01f * deltaTime;
    if(event->key() == Qt::Key_A)
    {
        m_moveDirection = "left";
    }
    else if(event->key() == Qt::Key_D)
    {
        m_moveDirection = "right";
    }
    else if(event->key() == Qt::Key_W)
    {
        m_moveDirection = "up";
    }
    else if(event->key() == Qt::Key_S)
    {
        m_moveDirection = "down";
    }

    QOpenGLWidget::keyPressEvent(event);
}

void Widget::keyReleaseEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_A)
    {
        m_moveDirection = "";
    }
    else if(event->key() == Qt::Key_D)
    {
        m_moveDirection = "";
    }
    else if(event->key() == Qt::Key_W)
    {
        m_moveDirection = "";
    }
    else if(event->key() == Qt::Key_S)
    {
        m_moveDirection = "";
    }
    QOpenGLWidget::keyReleaseEvent(event);
}

void Widget::mousePressEvent(QMouseEvent *event)
{
    QOpenGLWidget::mousePressEvent(event);
}

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseReleaseEvent(event);
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    //    qInfo()<<event->pos();
    int xpos = event->pos().x();
    int ypos = event->pos().y();
    int titleHeight = 23;

    if(firstMouse<2)
    {
        firstMouse++;

        lastX = xpos;
        lastY = ypos;

        m_cursor->setPos(width()/2, height()/2); //初始化鼠标为中心位置
        changeCameraPos(0.0, 0.0);
        if(firstMouse == 1) return;
    }

    if(xpos == width()/2 && (ypos == height()/2 || ypos == height()/2 - titleHeight))
    {
        return;
    }
//    qInfo()<<xpos<<ypos<<width()/2<<height()/2;

    float xoffset = xpos - width()/2;
    float yoffset = (height()/2 - titleHeight) - ypos; // 注意这里是相反的，因为y坐标是从底部往顶部依次增大的

    changeCameraPos(xoffset, yoffset);

    m_cursor->setPos(width()/2, height()/2); //初始化鼠标为中心位置

    QOpenGLWidget::mousePressEvent(event);
}

void Widget::wheelEvent(QWheelEvent *event)
{
    //    qInfo()<<event->angleDelta();
    if(event->angleDelta().y() > 0)
    {
        // scroll up 120
    }
    else if(event->angleDelta().y() < 0)
    {
        // scroll down -120
    }

    if(fov >= 1.0f && fov <= 45.0f)
        fov -= event->angleDelta().y() / 120.0;

    if(fov <= 1.0f)
        fov = 1.0f;
    if(fov >= 45.0f)
        fov = 45.0f;

    QOpenGLWidget::wheelEvent(event);
}

void Widget::changeCameraPos(float xoffset, float yoffset)
{
//    qInfo()<<__FUNCTION__;
    float sensitivity = 0.1f; // 灵敏度
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw   += xoffset;
    pitch += yoffset;

    if(pitch > 89.0f)
        pitch =  89.0f;
    if(pitch < -89.0f)
        pitch = -89.0f;

    QVector3D front;
    front.setX(cos(pitch/180.0*M_PI) * cos(yaw/180.0*M_PI));
    front.setY(sin(pitch/180.0*M_PI));
    front.setZ(cos(pitch/180.0*M_PI) * sin(yaw/180.0*M_PI));
    cameraFront = front.normalized();

//        qInfo()<<event->pos() << xoffset << yoffset
//              << "===" << cameraFront
//              << pitch << yaw
//        <<"XXX" << cameraPos+cameraFront;
}

void Widget::timeout()
{
    float cameraSpeed = 0.01f * deltaTime;
    if(m_moveDirection == "left")
    {
        cameraPos -= QVector3D::crossProduct(cameraFront, cameraUp).normalized() * cameraSpeed;
    }
    else if(m_moveDirection == "right")
    {
        cameraPos += QVector3D::crossProduct(cameraFront, cameraUp).normalized() * cameraSpeed;
    }
    else if(m_moveDirection == "up")
    {
        cameraPos += cameraSpeed * cameraFront;
    }
    else if(m_moveDirection == "down")
    {
        cameraPos -= cameraSpeed * cameraFront;
    }
    cubeShader->viewPos = cameraPos;

    m_angle+=1.0;
    if(m_angle>360.0) m_angle = 0.0;
//    cubeShader->lightColor = QVector3D(sin(m_angle * 2.0f), sin(m_angle * 0.7f), sin(m_angle * 1.3f));
//    QMatrix4x4 lightPos;

//    cubeShader->pos = QVector3D( sin(m_angle/180.0*M_PI) * 10.0, 0.5f, cos(m_angle/180.0*M_PI) * 10.0);

    cubeShader->pos = cameraPos;
    cubeShader->dir = cameraFront;

//    lightPos.scale(QVector3D(0.2f, 0.2f, 0.2f));
//    lightPos.translate(cubeShader->pos);

//    lightShader->pos = lightPos;

    update();
}
